//
// Created by Luhang on 2023/10/11/011.
//
//日志格式化输出类

#ifndef LOGPROJECT_FORMATTER_H
#define LOGPROJECT_FORMATTER_H

#include <memory>
#include <vector>
#include <tuple>
#include "util.hpp"
#include "level.hpp"
#include "message.hpp"

namespace LuLog{
    //负责日志消息子项的获取及格式化
    class FormatItem{
    public:
        using ptr = std::shared_ptr<FormatItem>;
        virtual ~FormatItem(){}
        virtual void format(std::ostream &os, const LogMsg &msg) = 0;
    };
    //日志有效信息
    class MsgFormatItem: public FormatItem{
    public:
        MsgFormatItem(const std::string &str = ""){}
        void format(std::ostream &os, const LogMsg &msg) override{
            os << msg._payload;
        }
    };
    //日志等级
    class LevelFormatItem : public FormatItem {
    public:
        LevelFormatItem(const std::string &str = ""){}
        void format(std::ostream &os, const LogMsg &msg) override {
            os << LogLevel::toString(msg._level);
        }
    };
    //日志器名字
    class NameFormatItem : public FormatItem {
    public:
        NameFormatItem(const std::string &str = ""){}
        void format(std::ostream &os, const LogMsg &msg) override {
            os << msg._name;
        }
    };
    //线程ID
    class ThreadFormatItem : public FormatItem {
    public:
        ThreadFormatItem(const std::string &str = ""){}
        void format(std::ostream &os, const LogMsg &msg) override {
            os << msg._tid;
        }
    };
    //时间
    class TimeFormatItem : public FormatItem {
    private:
        std::string _format;
    public:
        TimeFormatItem(const std::string &format = "%H:%M:%S"):_format(format){
            if (format.empty()) _format = "%H:%M:%S";
        }
        void format(std::ostream &os, const LogMsg &msg) override {
            time_t t = msg._ctime;
            struct tm lt{};
            localtime_r(&t, &lt);
            char tmp[128];
            strftime(tmp, 127, _format.c_str(), &lt);
            os << tmp;
        }
    };
    //文件名
    class CFileFormatItem : public FormatItem {
    public:
        CFileFormatItem(const std::string &str = ""){}
        void format(std::ostream &os, const LogMsg &msg) override {
            os << msg._file;
        }
    };
    //行号
    class CLineFormatItem : public FormatItem {
    public:
        CLineFormatItem(const std::string &str = ""){}
        void format(std::ostream &os, const LogMsg &msg) override {
            os << msg._line;
        }
    };
    //制表符
    class TabFormatItem : public FormatItem {
    public:
        TabFormatItem(const std::string &str = ""){}
        void format(std::ostream &os, const LogMsg &msg) override {
            os << "\t";
        }
    };
    //换行符
    class NLineFormatItem : public FormatItem {
    public:
        NLineFormatItem(const std::string &str = ""){}
        void format(std::ostream &os, const LogMsg &msg) override {
            os << "\n";
        }
    };
    //其他
    class OtherFormatItem : public FormatItem {
    private:
        std::string _str;
    public:
        OtherFormatItem(const std::string &str = ""):_str(str){}
        void format(std::ostream &os, const LogMsg &msg) override {
            os << _str;
        }
    };

    class Formatter{
    public:
        using ptr = std::shared_ptr<Formatter>;
        /*
            %d 日期
            %T 缩进
            %t 线程id
            %p 日志级别
            %c 日志器名称
            %f 文件名
            %l 行号
            %m 日志有效消息
            %n 换行
        */
        // [时间{年-月-日 时:分:秒}][线程ID][日志级别][日志器名称][文件名:行号] 有效信息\n
        Formatter(const std::string &pattern = "[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n"):
            _pattern(pattern){
                assert(parsePattern());
            }

        const std::string pattern(){
            return _pattern;
        }

        //对msg进行格式化
        std::string format(const LogMsg &msg){
            std::stringstream ss;
            for(auto &it : _items){
                it->format(ss, msg);
            }
            return ss.str();
        }
        std::ostream& format(std::ostream &os, const LogMsg &msg){
            for(auto &it : _items){
                it->format(os, msg);
            }
            return os;
        }

        //根据不同的格式化字符创建不同的格式化子项对象
        FormatItem::ptr createItem(const std::string &fc, const std::string &subFmt){
            if(fc == "m")
                return FormatItem::ptr(new MsgFormatItem(subFmt));
            if (fc == "p") 
                return FormatItem::ptr(new LevelFormatItem(subFmt));
            if (fc == "c") 
                return FormatItem::ptr(new NameFormatItem(subFmt));
            if (fc == "t") 
                return FormatItem::ptr(new ThreadFormatItem(subFmt));
            if (fc == "n") 
                return FormatItem::ptr(new NLineFormatItem(subFmt));
            if (fc == "d") 
                return FormatItem::ptr(new TimeFormatItem(subFmt));
            if (fc == "f") 
                return FormatItem::ptr(new CFileFormatItem(subFmt));
            if (fc == "l") 
                return FormatItem::ptr(new CLineFormatItem(subFmt));
            if (fc == "T") 
                return FormatItem::ptr(new TabFormatItem(subFmt));

            return FormatItem::ptr();
        }

        //对格式化字符串进行解析
        bool parsePattern(){
            //每个要素三部分：
            //格式化字符 + 对应的子格式 + 数据类型(0非格式化字符/1格式化数据类型)
            std::vector<std::tuple<std::string, std::string, int>> array;
            std::string formatKey;//存放%后的格式化字符
            std::string formatVal;//存放格式化字符后{}中的子格式字符串
            std::string stringRow;//存放非格式化字符
            bool subFormatError;//判断{}是否匹配
            int pos = 0;
            while(pos < _pattern.size()){
                //处理非格式化字符
                if(_pattern[pos] != '%'){
                    stringRow.append(1, _pattern[pos++]);
                    continue;
                }
                //处理双%
                if(pos + 1 < _pattern.size() && _pattern[pos + 1] == '%'){
                    stringRow.append(1, '%');
                    pos += 2;
                    continue;
                }
                //保存非格式化字符及双%
                if(!stringRow.empty()){
                    array.push_back(std::make_tuple(stringRow, "", 0));
                    stringRow.clear();
                }

                //处理格式化字符
                pos += 1;//指向% -> 指向格式化字符
                if(pos < _pattern.size() && isalpha(_pattern[pos])){
                    formatKey = _pattern[pos];
                }
                else{
                    std::cout << &_pattern[pos - 1] << "位置附近格式错误" << std::endl;
                    return false;
                }
                //处理子格式化字符，即{}内
                pos += 1;//指向格式化字符 -> 指向{
                if(pos < _pattern.size() && _pattern[pos] == '{'){
                    subFormatError = true;
                    pos += 1;//指向{下个字符
                    while(pos < _pattern.size()){
                        if(_pattern[pos] == '}'){
                            subFormatError = false;
                            pos += 1;//指向}下个字符
                            break;
                        }
                        formatVal.append(1, _pattern[pos++]);
                    }
                }
                //保存子格式化字符
                array.push_back(std::make_tuple(formatKey, formatVal, 1));
                formatKey.clear();
                formatVal.clear();
            }

            if(subFormatError){
                std::cout << "{}对应出错" << std::endl;
                return false;
            }
            if(!stringRow.empty())
                array.push_back(std::make_tuple(stringRow, "", 0));
            if(!formatKey.empty())
                array.push_back(std::make_tuple(formatKey, formatVal, 1));

            //根据解析得到的数据初始化格式化子项数组成员
            for(auto &it : array){
                if(std::get<2>(it) == 0){
                    FormatItem::ptr fi(new OtherFormatItem(std::get<0>(it)));
                    _items.push_back(fi);
                }
                else{
                    FormatItem::ptr fi = createItem(std::get<0>(it), std::get<1>(it));
                    if(fi == nullptr){
                        std::cout << "没有相应格式化字符: %" << std::get<0>(it) << std::endl;
                        return false;
                    }
                    _items.push_back(fi);
                }
            }
            return true;
        }
    private:
        std::string _pattern;//保存日志输出的格式字符串
        std::vector<FormatItem::ptr> _items;//按序保存格式化字符串对应的格式化子项对象
    };
}

#endif //LOGPROJECT_FORMATTER_H
